package org.tof.hotel.jdbc;

import org.apache.log4j.Logger;
import org.tof.hotel.exception.CannotEstablishConnectionException;
import org.tof.hotel.exception.PoolOverloadException;

import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;

/**
 * Connections Pool class.<br>
 * Retrieves url,user and password info from <code>db.properties<code>
 */
public class Pool {

    private Properties dbProps = new Properties();
    private int maxConnections;
    private ArrayList<JConnection> jCons = new ArrayList<JConnection>();
    private static Logger logger = Logger.getLogger(Pool.class);

    /**
     * Creates connections pool.
     * Default <code>maxConnections</code> value is 10.
     */
    public Pool() {
        try {
            dbProps.load(Pool.class.getResourceAsStream("/org/tof/hotel/db.properties"));
            Class.forName("com.mysql.jdbc.Driver");
        } catch (IOException e) {
            logger.error(e.toString(), e);
        } catch (ClassNotFoundException e) {
            logger.error(e.toString(), e);
        }
        this.maxConnections = 10;
    }

    /**
     * Creates connections pool.
     *
     * @param maxConnections should be non-zero value
     */
    public Pool(int maxConnections) throws IOException, ClassNotFoundException {
        dbProps.load(Pool.class.getResourceAsStream("/org/tof/hotel/db.properties"));
        Class.forName("com.mysql.jdbc.Driver");
        this.maxConnections = maxConnections;
    }

    public void dispose() {
        for (JConnection jConn : jCons) {
            removeConnection(jConn);
        }
    }

    public int getActiveConnections() {
        int i = 0;
        for (JConnection jConn : jCons) {
            if (jConn.isInUse()) {
                ++i;
            }
        }
        return i;
    }

    public JConnection getConnection() throws PoolOverloadException, CannotEstablishConnectionException {
        //look for unused connection
        for (JConnection jConn : jCons) {
            if (jConn.lease()) {
                return jConn;
            }
        }
        //checking if connections number reached the limit
        if (jCons.size() == maxConnections) {
            throw new PoolOverloadException("Pool is overloaded");
        }
        //creating brand new connection
        JConnection jConn = null;
        try {
            jConn = new JConnection(DriverManager.getConnection(dbProps.getProperty("url"), dbProps));
            jConn.lease();
            jCons.add(jConn);
        } catch (SQLException e) {
            throw new CannotEstablishConnectionException();
        }
        return jConn;
    }

    public void removeConnection(JConnection jConn) {
        jCons.remove(jConn);
        try {
            jConn.close();
        } catch (SQLException e) {
            logger.warn(e.toString(), e);
        }
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    /**
     * Sets max number of connections in pool
     *
     * @param maxConnections should be non-zero
     */
    public void setMaxConnections(int maxConnections) {
        if (maxConnections != 0) {
            this.maxConnections = maxConnections;
        }
    }

    public void releaseConnection(JConnection jConn) {
        jConn.setInUse(false);
        logger.debug("Connection released");
    }
}
